﻿using System;
using System.Globalization;
using System.Linq;
using DataNews;
using DataNews.StandartInterfaces;
using Encryption;
using NewsService.DataTransferObjectMapper;
using NewsService.MessageBase;
using NewsService.Messages;
using ServiceNews.Messages;

namespace NewsService
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "ActionNewsService" in code, svc and config file together.
    // NOTE: In order to launch WCF Test Client for testing this service, please select ActionNewsService.svc or ActionNewsService.svc.cs at the Solution Explorer and start debugging.
    public class ActionNewsService : IActionNewsService
    {
        #region Parameter
        /// <summary>
        /// The NewsLog DAO
        /// </summary>
        private static readonly INewsLog NewsLogDao = DataAccess.NewsLog;

        // Session state variables
        /// <summary>
        /// The _access token
        /// </summary>
        private static string _accessToken;

        /// <summary>
        /// The _user name
        /// </summary>
        private static string _userName;

        #endregion

        #region Validate

        /// <summary>
        /// Gets unique session based token that is valid for the duration of the session.
        /// </summary>
        /// <param name="request">Token request message.</param>
        /// <returns>
        /// Token response message.
        /// </returns>
        public TokenResponse GetToken(TokenRequest request)
        {
            var response = new TokenResponse(request.RequestId);

            // Validate client tag only
            if (!ValidRequest(request, response, Validate.ClientTag))
                return response;

            // Note: these are session based and expire when session expires.
            _accessToken = Guid.NewGuid().ToString();
            //_shoppingCart = new ShoppingCart(_defaultShippingMethod);
            _userName = request.UserName;
            response.AccessToken = _accessToken;
            return response;
        }

        /// <summary>
        /// Validate 3 security levels for a request: ClientTag, AccessToken, and User Credentials
        /// </summary>
        /// <param name="request">The request message.</param>
        /// <param name="response">The response message.</param>
        /// <param name="validate">The validation that needs to take place.</param>
        /// <returns></returns>
        private bool ValidRequest(RequestBase request, ResponseBase response, Validate validate)
        {
            // Validate Client Tag. 
            // Hardcoded here. In production this should query a 'client' table in a database.
            if ((Validate.ClientTag & validate) == Validate.ClientTag)
            {
                if (request.ClientTag != "ABC123")
                {
                    response.Acknowledge = AcknowledgeType.Failure;
                    response.Message = "Unknown Client Tag";
                    return false;
                }
            }
            // Validate access token
            if ((Validate.AccessToken & validate) == Validate.AccessToken)
            {
                if (request.AccessToken != _accessToken)
                {
                    response.Acknowledge = AcknowledgeType.Failure;
                    response.Message = "Invalid or expired AccessToken. Call GetToken()";
                    return false;
                }
            }
            // Validate user credentials
            if ((Validate.UserCredentials & validate) == Validate.UserCredentials)
            {
                if (_userName == null)
                {
                    response.Acknowledge = AcknowledgeType.Failure;
                    response.Message = "Please login and provide user credentials before accessing these methods.";
                    return false;
                }
            }
            return true;
        }

        #endregion

        #region NewsLog

        /// <summary>
        /// Sets the NewsLogs.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns></returns>
        public NewsLogResponse SetNewsLogs(NewsLogRequest request)
        {
            var response = new NewsLogResponse(request.RequestId);
            object errorMessage = null;
            // Validate client tag, access token, and user credentials
            if (!ValidRequest(request, response, Validate.All))
                return response;
            // Transform customer data transfer object to customer business object
            var mapper = Mapper.FromDataTransferObject(request.Item);

            // Validate customer business rules
            if (request.Action != "Delete")
            {
                if (!mapper.Validate())
                {
                    response.Acknowledge = AcknowledgeType.Failure;
                    foreach (var error in mapper.ValidationErrors)
                        response.Message += error + Environment.NewLine;

                    return response;
                }
            }
            switch (request.Action)
            {
                case "Insert":
                    {
                        errorMessage = NewsLogDao.CreateNewsLog(mapper);
                        break;
                    }
                case "Update":
                    errorMessage = NewsLogDao.UpdateNewsLog(mapper);
                    break;
                case "Delete":
                    var criteria = request.Criteria;
                    errorMessage = NewsLogDao.DeleteNewsLog(criteria.Id);
                    break;
            }
            if (errorMessage != null)
            {
                response.Acknowledge = AcknowledgeType.Failure;
                response.Message = errorMessage.ToString();
                return response;
            }
            response.Acknowledge = AcknowledgeType.Success;
            return response;
        }

        /// <summary>
        /// Gets the NewsLogs.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns></returns>
        public NewsLogResponse GetNewsLogs(NewsLogRequest request)
        {
            var response = new NewsLogResponse(request.RequestId);
            var criteria = request.Criteria;
            // Validate client tag, access token, and user credentials
            if (!ValidRequest(request, response, Validate.All))
                return response;
            if (request.LoadOptions.Contains("NewsLogs"))
            {
                if (request.OptionTypes == OptionType.IsPaging)
                {
                    var totalPage = request.TotalPage;
                    var objs = NewsLogDao.GetPagingNewsLog(request.CurrentPage, ref totalPage);
                    response.TotalPage = totalPage;
                    response.Items = Mapper.ToDataTransferObjects(objs);
                }
                else
                {
                    var objs = NewsLogDao.GetAllNewsLog();
                    response.Items = Mapper.ToDataTransferObjects(objs);
                }
            }
            
            if (request.LoadOptions.Contains("NewsLog"))
            {
                var obj = NewsLogDao.GetNewsLog(criteria.Id);
                response.Item = Mapper.ToDataTransferObject(obj);
            }
            
            return response;
        }

        #endregion


        #region Primary key encryption methods. Not currently used.

        /// <summary>
        /// Encrypts internal identifier before sending it out to client.
        /// Private helper method.
        /// </summary>
        /// <param name="id">Identifier to be encrypted.</param>
        /// <param name="tableName">Database table in which identifier resides.</param>
        /// <returns>
        /// Encrypted stringified identifier.
        /// </returns>
        private string EncryptId(int id, string tableName)
        {
            var s = id.ToString(CultureInfo.InvariantCulture) + "|" + tableName;
            return Crypto.ActionEncrypt(s);
        }

        /// <summary>
        /// Decrypts identifiers that come back from client.
        /// Private helper method.
        /// </summary>
        /// <param name="sid">Stringified, encrypted identifier.</param>
        /// <returns>
        /// Internal identifier.
        /// </returns>
        private int DecryptId(string sid)
        {
            var s = Crypto.ActionDecrypt(sid);
            s = s.Substring(0, s.IndexOf("|", System.StringComparison.Ordinal));
            return int.Parse(s);
        }

        #endregion

        /// <summary>
        /// Validation options enum. Used in validation of messages.
        /// </summary>
        [Flags]
        private enum Validate
        {
            /// <summary>
            /// The client tag
            /// </summary>
            ClientTag = 0x0001,
            /// <summary>
            /// The access token
            /// </summary>
            AccessToken = 0x0002,
            /// <summary>
            /// The user credentials
            /// </summary>
            UserCredentials = 0x0004,
            /// <summary>
            /// All
            /// </summary>
            All = ClientTag | AccessToken | UserCredentials
        }
    }
}
